package ru.ksu.ktk.diploma.sdi.util;

import java.util.*;

import com.hp.hpl.jena.ontology.*;
import com.hp.hpl.jena.rdf.model.Literal;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.util.iterator.ExtendedIterator;
import com.hp.hpl.jena.vocabulary.RDFS;

public class OntClassUtil {
    private static final String INSTANCES_DELIMITER = " ";

    private OntClassUtil() {
    }

    /**
     * get text content of given ontology class' instances as concatenated string
     * 
     * @param sourceClass ontology class
     * @return
     */
    public static String getInstancesTextContent(OntClass sourceClass) {
        StringBuilder instanceTextBuilder = new StringBuilder();
        ExtendedIterator instanceIterator = sourceClass.listInstances();
        while (instanceIterator.hasNext()){
            RDFNode rdfLabelProperty = getRdfLabelProperty(instanceIterator);
            if (rdfLabelProperty != null && rdfLabelProperty.isLiteral()){
                String text = ((Literal) rdfLabelProperty).getString();
                instanceTextBuilder.append(INSTANCES_DELIMITER);
                instanceTextBuilder.append(text);
            }
        }
        String instanceText = instanceTextBuilder.toString();
        return instanceText;
    }

    private static RDFNode getRdfLabelProperty(ExtendedIterator instanceIterator) {
        Individual individual = (Individual) instanceIterator.next();
        RDFNode rdfLabelProperty = individual.getPropertyValue(RDFS.label);
        return rdfLabelProperty;
    }

    /**
     * get all super-classes of given class this method is useful if logic reasoner is disabled
     * 
     * @param ontClass ontology class
     * @return
     */
    public static List<OntClass> getAllSuperClasses(OntClass ontClass) {
        List<OntClass> superClasses = new ArrayList<OntClass>();
        Stack<OntClass> stack = new Stack<OntClass>();
        stack.push(ontClass);
        while (!stack.isEmpty()){
            OntClass curOntClass = stack.pop();
            if (!curOntClass.equals(ontClass)){
                superClasses.add(curOntClass);
            }
            ExtendedIterator superClassIterator = curOntClass.listSuperClasses(true);
            while (superClassIterator.hasNext()){
                OntClass superClass = (OntClass) superClassIterator.next();
                stack.push(superClass);
            }
        }
        return superClasses;
    }

    public static List<OntProperty> getAllSuperProperties(OntProperty ontProperty) {
        List<OntProperty> superProperties = new ArrayList<OntProperty>();
        Stack<OntProperty> stack = new Stack<OntProperty>();
        stack.push(ontProperty);
        while (!stack.isEmpty()){
            OntProperty curOntProperty = stack.pop();
            if (!curOntProperty.equals(ontProperty)){
                superProperties.add(curOntProperty);
            }
            ExtendedIterator superPropertiesIterator = curOntProperty.listSuperProperties(true);
            while (superPropertiesIterator.hasNext()){
                OntProperty superProperty = (OntProperty) superPropertiesIterator.next();
                if (!superProperty.equals(curOntProperty)){
                    stack.push(superProperty);
                }
            }
        }
        return superProperties;
    }

    public static List<OntProperty> getAllSubProperties(OntProperty ontProperty) {
        List<OntProperty> subProperties = new ArrayList<OntProperty>();
        Stack<OntProperty> stack = new Stack<OntProperty>();
        stack.push(ontProperty);
        while (!stack.isEmpty()){
            OntProperty curOntProperty = stack.pop();
            if (!curOntProperty.equals(ontProperty)){
                subProperties.add(curOntProperty);
            }
            ExtendedIterator subPropertiesIterator = curOntProperty.listSubProperties(true);
            while (subPropertiesIterator.hasNext()){
                OntProperty subProperty = (OntProperty) subPropertiesIterator.next();
                if (!subProperty.equals(curOntProperty)){
                    stack.push(subProperty);
                }
            }
        }
        return subProperties;
    }

    /**
     * retrieve all properties between given subject and object, concerned as frame
     * 
     * @param subject
     * @param object
     * @return
     */
    public static List<OntProperty> retrieveProperties(OntClass subject, OntClass object) {
        OntPropertyListAdapter properties = new OntPropertyListAdapter();
        OntClassListAdapter objectSuperClasses = new OntClassListAdapter(getAllSuperClasses(object));
        List<OntClass> subjectClasses = new ArrayList<OntClass>();
        subjectClasses.add(subject);
        subjectClasses.addAll(getAllSuperClasses(subject));
        for (OntClass subjectClass : subjectClasses){
            ExtendedIterator propertiesIterator = subjectClass.listDeclaredProperties();
            while (propertiesIterator.hasNext()){

                OntProperty property = (OntProperty) propertiesIterator.next();
                OntResource range = property.getRange();
                boolean hasEqualRange = objectSuperClasses.contains(range) || object.getURI().equals(range.getURI());
                if (hasEqualRange){
                    properties.add(property);
                }
            }
        }

        return properties.toList();
    }

    private static class OntClassListAdapter {
        private List<OntClass> list;

        public OntClassListAdapter(List<OntClass> list) {
            this.list = list;
        }

        public List<OntClass> toList() {
            return list;
        }

        public boolean contains(OntResource resource) {
            for (OntClass ontClass : list){
                if (ontClass.getURI().equals(resource.getURI())){
                    return true;
                }
            }
            return false;
        }
    }

    private static class OntPropertyListAdapter {
        private List<OntProperty> list = new ArrayList<OntProperty>();

        public List<OntProperty> toList() {
            return list;
        }

        public void add(OntProperty property) {
            if (!foundSubProperty(property) && !foundProperty(property)){
                removeSuperProperties(property);
                list.add(property);
            }

        }

        private boolean foundProperty(OntProperty property) {
            return list.contains(property);
        }

        private boolean foundSubProperty(OntProperty property) {
            List<OntProperty> subProperties = getAllSubProperties(property);
            for (OntProperty ontProperty : list){
                if (subProperties.contains(ontProperty)){
                    return true;
                }
            }
            return false;
        }

        private void removeSuperProperties(OntProperty property) {
            List<OntProperty> superProperties = getAllSuperProperties(property);
            List<OntProperty> removingProperties = new ArrayList<OntProperty>();
            for (OntProperty ontProperty : list){
                if (superProperties.contains(ontProperty)){
                    removingProperties.add(ontProperty);
                }
            }
            for (OntProperty removingProperty : removingProperties){
                list.remove(removingProperty);
            }
        }

    }

}
